#include "bigm.h"

void syncDirectories(const char* sourceDir, const char* destDir);
void syncFiles(const char* fileA, const char* fileB);
extern void showProgressBar(int progress);
extern void copyFile(const char* srcPath, const char* destPath);

// 删除文件或目录
void deleteEntry(const char* entryPath) {
    struct stat statBuf;
    if (stat(entryPath, &statBuf) == 0) {
        if (S_ISDIR(statBuf.st_mode)) {
            // 如果是目录，递归删除目录及其内容
            DIR* dir = opendir(entryPath);
            if (dir) {
                struct dirent* entry;
                while ((entry = readdir(dir)) != NULL) {
                    if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
                        char childPath[1024];
                        snprintf(childPath, sizeof(childPath), "%s/%s", entryPath, entry->d_name);
                        deleteEntry(childPath);
                    }
                }
                closedir(dir);
            }
            rmdir(entryPath);
        } else {
            // 如果是文件，直接删除文件
            remove(entryPath);
        }
    }
}

// 单向同步目录
void syncDirectories(const char* sourceDir, const char* destDir) {
    // 删除目标目录中存在但源目录中不存在的文件或目录
    DIR* destDirPtr = opendir(destDir);
    if (destDirPtr) {
        struct dirent* destEntry;
        while ((destEntry = readdir(destDirPtr)) != NULL) {
            if (strcmp(destEntry->d_name, ".") == 0 || strcmp(destEntry->d_name, "..") == 0) {
                continue;
            }

            char destPath[1024];
            snprintf(destPath, sizeof(destPath), "%s/%s", destDir, destEntry->d_name);

            char sourcePath[1024];
            snprintf(sourcePath, sizeof(sourcePath), "%s/%s", sourceDir, destEntry->d_name);

            struct stat sourceStat;
            if (stat(sourcePath, &sourceStat) != 0) {
                deleteEntry(destPath);
            }
        }
        closedir(destDirPtr);
    }

    // 复制源目录中存在但目标目录中不存在的文件或目录，并处理子目录
    DIR* sourceDirPtr = opendir(sourceDir);
    if (sourceDirPtr) {
        struct dirent* sourceEntry;
        while ((sourceEntry = readdir(sourceDirPtr)) != NULL) {
            if (strcmp(sourceEntry->d_name, ".") == 0 || strcmp(sourceEntry->d_name, "..") == 0) {
                continue;
            }

            char sourcePath[1024];
            snprintf(sourcePath, sizeof(sourcePath), "%s/%s", sourceDir, sourceEntry->d_name);

            char destPath[1024];
            snprintf(destPath, sizeof(destPath), "%s/%s", destDir, sourceEntry->d_name);

            struct stat sourceStat;
            if (stat(sourcePath, &sourceStat) == 0) {
                if (S_ISDIR(sourceStat.st_mode)) {
                    // 如果源路径是目录，递归同步子目录
                    mkdir(destPath, 0777);  // 创建目标目录
                    syncDirectories(sourcePath, destPath);
                } else if (S_ISREG(sourceStat.st_mode)) {
                    // 如果源路径是文件，则复制文件到目标目录
                    if (sourceStat.st_size > FILE_COPY_THRESHOLD) {
                        // 使用多线程备份单个文件
                        pthread_t thread;
                        pthread_create(&thread, NULL, (void* (*)(void*))syncFiles, (void*)sourcePath);
                        pthread_join(thread, NULL);
                    } else {
                        // 使用单个进程备份单个文件
                        syncFiles(sourcePath, destPath);
                    }
                }
            }
        }
        closedir(sourceDirPtr);
    }
}

// 双向同步函数
void two(const char* dirA, const char* dirB) {
    DIR* dir;
    struct dirent* entry;

    // 打开目录A
    dir = opendir(dirA);
    if (dir == NULL) {
        printf("无法打开目录 %s\n", dirA);
        return;
    }

    // 遍历目录A中的文件和目录
    while ((entry = readdir(dir)) != NULL) {
        char pathA[1024];
        char pathB[1024];

        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        // 构建目录A和目录B中文件或目录的完整路径
        snprintf(pathA, sizeof(pathA), "%s/%s", dirA, entry->d_name);
        snprintf(pathB, sizeof(pathB), "%s/%s", dirB, entry->d_name);

        // 检查是否为目录
        struct stat statbuf;
        if (stat(pathA, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
            // 如果目录B中不存在该目录，则创建目录
            if (access(pathB, F_OK) == -1) {
                mkdir(pathB, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
            }
            // 递归同步目录
            two(pathA, pathB);
        } else {
            // 如果当前项是文件，则判断是否需要多线程方式备份
            if (statbuf.st_size > FILE_COPY_THRESHOLD) {
                // 使用多线程备份单个文件
                pthread_t thread;
                pthread_create(&thread, NULL, (void* (*)(void*))syncFiles, (void*)pathA);
                pthread_join(thread, NULL);
            } else {
                // 使用单个进程备份单个文件
                syncFiles(pathA, pathB);
            }
        }
    }

    closedir(dir);
}

// 按修改时间最新同步
void syncFiles(const char* fileA, const char* fileB) {
    struct stat statA, statB;

    // 检查文件A是否存在
    if (stat(fileA, &statA) != 0) {
        printf("文件 %s 不存在\n", fileA);
        return;
    }

    // 检查文件B是否存在
    if (stat(fileB, &statB) != 0) {
        // 文件B不存在，将文件A复制到文件B
        FILE* src = fopen(fileA, "rb");
        FILE* dst = fopen(fileB, "wb");

        if (src == NULL || dst == NULL) {
            printf("无法打开文件进行复制!\n");
            return;
        }
        
        char buffer[4096];
	    size_t bytesRead;
	    long long totalSize = 0;
	    long long copiedSize = 0;

	    fseek(src, 0, SEEK_END);
	    totalSize = ftell(src);
	    fseek(src, 0, SEEK_SET);
	
	    while ((bytesRead = fread(buffer, 1, sizeof(buffer), src)) > 0) {
	        fwrite(buffer, 1, bytesRead, dst);
	        copiedSize += bytesRead;
	
	        int progress = (int) ((copiedSize * 100) / totalSize);
	        
	        showProgressBar(progress);
	        fflush(stdout);
	    }

        fclose(src);
        fclose(dst);
    } else {
        // 文件B存在，比较文件内容和修改时间
        if (statA.st_mtime > statB.st_mtime) {
            // 文件A的修改时间较新，将文件A复制到文件B
            FILE* src = fopen(fileA, "rb");
            FILE* dst = fopen(fileB, "wb");

            if (src == NULL || dst == NULL) {
                printf("无法打开文件进行复制!\n");
                return;
            }
            
            char buffer[4096];
	    	size_t bytesRead;
	    	long long totalSize = 0;
	    	long long copiedSize = 0;

	    	fseek(src, 0, SEEK_END);
	    	totalSize = ftell(src);
	    	fseek(src, 0, SEEK_SET);
	
	    	while ((bytesRead = fread(buffer, 1, sizeof(buffer), src)) > 0) {
	        	fwrite(buffer, 1, bytesRead, dst);
	        	copiedSize += bytesRead;
	
	        	int progress = (int) ((copiedSize * 100) / totalSize);
	        
	        	showProgressBar(progress);
	        	fflush(stdout);
	    	}
	    	
            fclose(src);
            fclose(dst);
        }
    }
}

// 同步单份输入提示
void E() {
    char a[100];
    char b[100];
    int n;
    int flag1 = 0;

    printf("请输入第一个文件：");
    scanf("%s", a);
	printf("请输入第二个文件：");
    scanf("%s", b);
    printf("\n");
    
    printf("请选择想要的同步方向:\n");
    printf("1. 前一个文件同步到后一个文件\n");
    printf("2. 后一个文件同步到前一个文件\n");
    printf("3. 双向同步（保留修改时间新的文件）\n");
    printf("请输入1、2或3进行选择：\n");
    scanf("%d", &n);
    
    switch(n) {
            case 1:
                syncDirectories(a, b);
            	break;
            case 2:
            	syncDirectories(b, a);
            	break;
            case 3:
            	two(a, b);
            	two(b, a);
            	break;
            default:
            	flag1 = 1;
            	printf("无效选择，请重试！\n\n");
	}
    
    if (flag1 == 0) {
		printf("同步任务完成! [\n");
	} else {
		flag1 = 0;
	}
}

// 同步多份输入提示
void F() {
    // 声明任务列表数组
    char tasks[MAX_TASKS][2][MAX_PATH_LENGTH];
    // 记录任务数
    int numTasks = 0;
    int n;
    int l[10];
    int flag = 0;
    int flag1 = 0;

    // 循环获取任务信息，直到达到最大任务数或输入结束标志
    while (numTasks < MAX_TASKS) {
        // 输入源目录路径
        printf("请输入想要同步的第一个文件（结束请输入'q'）：");
        if (scanf("%s", tasks[numTasks][0]) != 1 || tasks[numTasks][0][0] == 'q') {
            break;
        }

        // 输入目标目录路径
        printf("请输入想要同步的第二个文件：");
        scanf("%s", tasks[numTasks][1]);
        
        printf("\n请选择想要的同步方向:\n");
        printf("1. 前一个文件同步到后一个文件\n");
    	printf("2. 后一个文件同步到前一个文件\n");
    	printf("3. 双向同步（保留修改时间新的文件）\n");
    	printf("请输入1、2或3进行选择：\n");
    	scanf("%d", &n);
    	printf("\n");
    	
    	switch(n) {
            case 1:
                flag = 1;
            	break;
            case 2:
            	flag = 2;
            	break;
            case 3:
            	flag = 3;
            	break;
            default:
            	flag1 = 1;
            	printf("无效选择，请重试！\n\n");
        }
        
        l[numTasks] = flag;
        // 增加任务数
        numTasks++;
        flag = 0;
    }

    // 多进程备份任务
    int processesCount = 0; // 当前进行的进程数
    for (int i = 0; i < numTasks; i++) {
        pid_t pid = fork();
        if (pid < 0) {
            printf("创建子进程时发生错误!\n");
        } else if (pid == 0) {
            // 子进程中进行备份任务
            if (l[i] == 1){
            	syncDirectories(tasks[i][0], tasks[i][1]);
            } else if (l[i] == 2) {
            	syncDirectories(tasks[i][1], tasks[i][0]);
            } if (l[i] == 3) {
            	two(tasks[i][0], tasks[i][1]);
            	two(tasks[i][1], tasks[i][0]);
            }
            
            exit(0); // 子进程完成任务后退出
        } else {
            processesCount++;
            if (processesCount >= MAX_PROCESSES) {
                // 达到最大进程数，等待其中一个进程结束
                wait(NULL);
                processesCount--;
            }
        }
    }

    // 等待所有进程结束
    while (processesCount > 0) {
        wait(NULL);
        processesCount--;
    }
	
	if (flag1 == 0) {
		printf("同步任务完成! [\n");
	} else {
		flag1 = 0;
	}
}

// 同步程序入口
void tb() {
    int n;
    
    printf("\n---------------------文件同步---------------------\n\n");
    
    do {
        printf("请选择想要同步的模式：\n");
        printf("1. 同步单份\n");
        printf("2. 同步多份\n");
        printf("3. 退出\n");
        printf("请输入1、2或3进行选择:\n");
        scanf("%d", &n);
        printf("\n---------------------------------------------------\n");
        
        switch(n) {
            case 1:
                E();
            	break;
            case 2:
            	F();
            	break;
            case 3:
            	printf("文件同步程序退出！\n");
				return ;
            default:
            	printf("无效选择，请重试！\n\n");
            	continue;
        }
        
        printf("\n");
    }while (1);
}